#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/stat.h>

#define PTRSIZE 128
#define BUFSIZE 256

// GPIO硬件可变信息,每一组GPIO对应一个此结构
struct gpio_desc {
	int group;
	int num[16];
};
// E2000D 6组GPIO
static struct gpio_desc gpio_val[6];

// E2000D所有的GPIO编号都存储到gpio_val中
void gpio_num_init(void);
// group组的第n号引脚的编号
int gpio_get(int group, int n);
// 将value写入配置文件pptr/attr
int gpio_write(char *pptr, char *attr, const char value);

int open_gpio_attr(int num, const char *attr, mode_t mode);

int set_gpio_attr(int num, const char *attr, const char *value);

int check_gpio_dir(int num);

int create_gpio_dir(int num);

int get_led_gpio_num(int index);

int open_pwm_attr(int num, const char *attr);

int check_pwm_dir(int num);

int create_pwm_dir(int num);

int main(int argc, char *argv[])
{
	char pptr[PTRSIZE] = {};
	char buf[BUFSIZE] = {};
	int key;
	int led, index = 0;
	int fd;
	int res = 0;

	struct pollfd pfd;

	if (argc < 3 || strcmp(argv[1], "4") || (strcmp(argv[2], "1") && strcmp(argv[2], "3"))) {
		fprintf(stderr, "Usage: ./key group_num pin_num\n");
		fprintf(stderr, "./key 4 1\n");
		fprintf(stderr, "./key 4 3\n");
		exit(1);
	}

	// 获取对应gpio的编号
	gpio_num_init();
	key = gpio_get(atoi(argv[1]), atoi(argv[2]));

	if (check_gpio_dir(key)) {
		res = create_gpio_dir(key);
	}
	if (res != 0) {
		exit(1);
	}

	set_gpio_attr(key, "edge", "both");
	set_gpio_attr(key, "direction", "in");
	
	fd = open_gpio_attr(key, "value", O_RDONLY);
	if (-1 == fd) {
		exit(1);
	}
	pfd.fd = fd;
	pfd.events = POLLPRI; // 中断
	
	memset(buf, 0, BUFSIZE);
	if (-1 == read(fd, buf, BUFSIZE)) {
		perror("Read file error");
		close(fd);
		exit(1);
	}
	printf("Value: %s\n", buf);
	printf("Press the key...\n");

	while (1) {
		if (-1 == poll(&pfd, 1, -1)) {
			perror("poll()");
			close(fd);
			exit(1);
		}
		if (pfd.revents & POLLPRI) {
			lseek(fd, 0, SEEK_SET);
			memset(buf, 0, BUFSIZE);
			if (-1 == read(fd, buf, BUFSIZE)) {
				perror("read()");
				close(fd);
				exit(1);
			}
			printf("Key interrupt event\n");
			printf("Key value is: %s\n", buf);

			led = get_led_gpio_num(index);
			if (-1 == led) {
				perror("Unknown LED index");
				exit(1);
			}

			if (check_gpio_dir(led)) {
				res = create_gpio_dir(led);
			}
			if (res != 0) {
				exit(1);
			}

			if (buf[0] == '0') {
				printf("LED ON!\n");
				set_gpio_attr(led, "active_low", "0");
				set_gpio_attr(led, "direction", "out");
				set_gpio_attr(led, "value", "1");
			} else {
				printf("LED OFF!\n");
				set_gpio_attr(led, "value", "0");
				// Next light
				index = (index + 1) % 3;
			}
		}
	}

	exit(0);
}

void gpio_num_init(void)
{
	int i, j;

	// 组
	for (i = 0; i < 6; i++) {
		gpio_val[i].group = i;
		// 引脚
		for (j = 0; j < 16; j++) {
			// 16 * i每一组引脚编号起始值496 - 16 * i
			gpio_val[i].num[j] = 496 - 16 * i + j;
		}
	}
}

int gpio_get(int group, int n)
{
	return gpio_val[group].num[n];
}

int open_gpio_attr(int num, const char *attr, mode_t mode)
{
	char path[PTRSIZE] = {};

	snprintf(path, PTRSIZE, "/sys/class/gpio/gpio%d/%s", num, attr);
	return open(path, mode);
}

int set_gpio_attr(int num, const char *attr, const char *value)
{
	int fd = open_gpio_attr(num, attr, O_WRONLY);
	if (-1 == fd) {
		perror("Error opening GPIO attribute file");
		return -1;
	}
	printf("Write \"%s\" into [%d]/%s\n", value, num, attr);

	write(fd, value, strlen(value));
	close(fd);

	return 0;
}

int check_gpio_dir(int num)
{
	char path[PTRSIZE] = {};
	
	snprintf(path, PTRSIZE, "/sys/class/gpio/gpio%d", num);
	return access(path, F_OK);
}

int create_gpio_dir(int num)
{
	char gpio_num[10] = {};
	int export_fd = open("/sys/class/gpio/export", O_WRONLY);

	if (-1 == export_fd) {
		perror("Error opening GPIO export file");
		return -1;
	}
	printf("Creating GPIO dir %d\n", num);

	snprintf(gpio_num, 10, "%d", num);
	write(export_fd, gpio_num, strlen(gpio_num));

	close(export_fd);

	return 0;
}

int get_led_gpio_num(int index) {
	switch (index) {
		case 0:
			return gpio_get(0, 13);
			break;
		case 1:
			return gpio_get(1, 1);
			break;
		case 2:
			return gpio_get(5, 0);
		default:
			return -1;
	}
}

int open_pwm_attr(int num, const char *attr, mode_t mode)
{
	char path[PTRSIZE] = {};

	snprintf(path, PTRSIZE, "/sys/class/pwm/pwmchip%d/pwm%d/%s", num, num, attr);
	return open(path, mode);
}

int set_pwm_attr(int num, const char *attr, const char *value)
{
	int fd = open_pwm_attr(num, attr, O_WRONLY);
	if (-1 == fd) {
		perror("Error opening GPIO attribute file");
		return -1;
	}
	printf("Write \"%s\" into [%d]/%s\n", value, num, attr);

	write(fd, value, strlen(value));
	close(fd);

	return 0;
}

int check_pwm_dir(int num)
{
	char path[PTRSIZE] = {};
	
	snprintf(path, PTRSIZE, "/sys/class/pwm/pwmchip%d/pwm%d", num, num);
	return access(path, F_OK);
}

int create_pwm_dir(int num)
{
	char path[PTRSIZE] = {};
	char pwm_num[10] = {};
	int export_fd;

	snprintf(path, PTRSIZE, "/sys/class/pwm/pwmchip%d/export", num);
	export_fd = open(path, O_WRONLY);

	if (-1 == export_fd) {
		perror("Error opening PWM export file");
		return -1;
	}
	printf("Creating PWM dir %d\n", num);

	snprintf(pwm_num, 10, "%d", num);
	write(export_fd, pwm_num, strlen(pwm_num));
	close(export_fd);

	return 0;
}

